#region File Description
//-----------------------------------------------------------------------------
// InputState.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

#region Using Statements
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using System;
using System.Linq;
#endregion

namespace Congkak
{
    /// <summary>
    /// Helper for reading input from keyboard and gamepad. This class tracks both
    /// the current and previous state of both input devices, and implements query
    /// methods for high level input actions such as "move up through the menu"
    /// or "pause the game".
    /// </summary>
    public class InputState
    {
        #region Fields

        public const int MaxInputs = 4;

        public readonly KeyboardState[] CurrentKeyboardStates;
        public readonly GamePadState[] CurrentGamePadStates;

        public readonly KeyboardState[] LastKeyboardStates;
        public readonly GamePadState[] LastGamePadStates;

        public MouseState CurrentMouseState;
        public MouseState LastMouseState;

        public readonly bool[] GamePadWasConnected;

        #endregion

        #region Initialization


        /// <summary>
        /// Constructs a new input state.
        /// </summary>
        public InputState()
        {
            CurrentKeyboardStates = new KeyboardState[MaxInputs];
            CurrentGamePadStates = new GamePadState[MaxInputs];

            LastKeyboardStates = new KeyboardState[MaxInputs];
            LastGamePadStates = new GamePadState[MaxInputs];

            CurrentMouseState = new MouseState();
            LastMouseState = new MouseState();

            GamePadWasConnected = new bool[MaxInputs];
        }


        #endregion

        //#region Events
        //public event EventHandler<MouseEventArgs> MouseUp;
        //public event EventHandler<MouseEventArgs> MouseDown;
        //public event EventHandler<MouseEventArgs> MouseClick;
        //#endregion
        #region Public Methods


        /// <summary>
        /// Reads the latest state of the keyboard and gamepad.
        /// </summary>
        public void Update()
        {
            for (int i = 0; i < MaxInputs; i++)
            {
                LastKeyboardStates[i] = CurrentKeyboardStates[i];
                LastGamePadStates[i] = CurrentGamePadStates[i];

                CurrentKeyboardStates[i] = Keyboard.GetState((PlayerIndex)i);
                CurrentGamePadStates[i] = GamePad.GetState((PlayerIndex)i);

                // Keep track of whether a gamepad has ever been
                // connected, so we can detect if it is unplugged.
                if (CurrentGamePadStates[i].IsConnected)
                {
                    GamePadWasConnected[i] = true;
                }
            }

            LastMouseState = CurrentMouseState;
            CurrentMouseState = Mouse.GetState();

            //if (CurrentMouseState != LastMouseState)
            //{
            //    MouseButton button;
            //    int X, Y;

            //    if (IsMouseDown(out button, out X, out Y))
            //    {
            //        if (MouseDown != null)
            //            MouseDown(this, new MouseEventArgs(button, new Point(X, Y)));
            //    }
            //    if (IsMouseUp(out button, out X, out Y))
            //    {
            //        if (MouseUp != null)
            //            MouseUp(this, new MouseEventArgs(button, new Point(X, Y)));
            //    }
            //    if (IsMouseClick(out button, out X, out Y))
            //    {
            //        if (MouseClick != null)
            //            MouseClick(this, new MouseEventArgs(button, new Point(X, Y)));
            //    }
            //}
        }


        /// <summary>
        /// Helper for checking if a key was newly pressed during this update. The
        /// controllingPlayer parameter specifies which player to read input for.
        /// If this is null, it will accept input from any player. When a keypress
        /// is detected, the output playerIndex reports which player pressed it.
        /// </summary>
        public bool IsNewKeyPress(Keys key, PlayerIndex? controllingPlayer,
                                            out PlayerIndex playerIndex)
        {
            if (controllingPlayer.HasValue)
            {
                // Read input from the specified player.
                playerIndex = controllingPlayer.Value;

                int i = (int)playerIndex;

                return (CurrentKeyboardStates[i].IsKeyDown(key) &&
                        LastKeyboardStates[i].IsKeyUp(key));
            }
            else
            {
                // Accept input from any player.
                return (IsNewKeyPress(key, PlayerIndex.One, out playerIndex) ||
                        IsNewKeyPress(key, PlayerIndex.Two, out playerIndex) ||
                        IsNewKeyPress(key, PlayerIndex.Three, out playerIndex) ||
                        IsNewKeyPress(key, PlayerIndex.Four, out playerIndex));
            }
        }


        /// <summary>
        /// Helper for checking if a button was newly pressed during this update.
        /// The controllingPlayer parameter specifies which player to read input for.
        /// If this is null, it will accept input from any player. When a button press
        /// is detected, the output playerIndex reports which player pressed it.
        /// </summary>
        public bool IsNewButtonPress(Buttons button, PlayerIndex? controllingPlayer,
                                                     out PlayerIndex playerIndex)
        {
            if (controllingPlayer.HasValue)
            {
                // Read input from the specified player.
                playerIndex = controllingPlayer.Value;

                int i = (int)playerIndex;

                return (CurrentGamePadStates[i].IsButtonDown(button) &&
                        LastGamePadStates[i].IsButtonUp(button));
            }
            else
            {
                // Accept input from any player.
                return (IsNewButtonPress(button, PlayerIndex.One, out playerIndex) ||
                        IsNewButtonPress(button, PlayerIndex.Two, out playerIndex) ||
                        IsNewButtonPress(button, PlayerIndex.Three, out playerIndex) ||
                        IsNewButtonPress(button, PlayerIndex.Four, out playerIndex));
            }
        }


        /// <summary>
        /// Checks for a "menu select" input action.
        /// The controllingPlayer parameter specifies which player to read input for.
        /// If this is null, it will accept input from any player. When the action
        /// is detected, the output playerIndex reports which player pressed it.
        /// </summary>
        public bool IsMenuSelect(PlayerIndex? controllingPlayer,
                                 out PlayerIndex playerIndex)
        {
            return IsNewKeyPress(Keys.Space, controllingPlayer, out playerIndex) ||
                   IsNewKeyPress(Keys.Enter, controllingPlayer, out playerIndex) ||
                   IsNewButtonPress(Buttons.A, controllingPlayer, out playerIndex) ||
                   IsNewButtonPress(Buttons.Start, controllingPlayer, out playerIndex);
        }


        /// <summary>
        /// Checks for a "menu cancel" input action.
        /// The controllingPlayer parameter specifies which player to read input for.
        /// If this is null, it will accept input from any player. When the action
        /// is detected, the output playerIndex reports which player pressed it.
        /// </summary>
        public bool IsMenuCancel(PlayerIndex? controllingPlayer,
                                 out PlayerIndex playerIndex)
        {
            return IsNewKeyPress(Keys.Escape, controllingPlayer, out playerIndex) ||
                   IsNewButtonPress(Buttons.B, controllingPlayer, out playerIndex) ||
                   IsNewButtonPress(Buttons.Back, controllingPlayer, out playerIndex);
        }


        /// <summary>
        /// Checks for a "menu up" input action.
        /// The controllingPlayer parameter specifies which player to read
        /// input for. If this is null, it will accept input from any player.
        /// </summary>
        public bool IsMenuUp(PlayerIndex? controllingPlayer)
        {
            PlayerIndex playerIndex;

            return IsNewKeyPress(Keys.Up, controllingPlayer, out playerIndex) ||
                   IsNewButtonPress(Buttons.DPadUp, controllingPlayer, out playerIndex) ||
                   IsNewButtonPress(Buttons.LeftThumbstickUp, controllingPlayer, out playerIndex);
        }


        /// <summary>
        /// Checks for a "menu down" input action.
        /// The controllingPlayer parameter specifies which player to read
        /// input for. If this is null, it will accept input from any player.
        /// </summary>
        public bool IsMenuDown(PlayerIndex? controllingPlayer)
        {
            PlayerIndex playerIndex;

            return IsNewKeyPress(Keys.Down, controllingPlayer, out playerIndex) ||
                   IsNewButtonPress(Buttons.DPadDown, controllingPlayer, out playerIndex) ||
                   IsNewButtonPress(Buttons.LeftThumbstickDown, controllingPlayer, out playerIndex);
        }


        /// <summary>
        /// Checks for a "pause the game" input action.
        /// The controllingPlayer parameter specifies which player to read
        /// input for. If this is null, it will accept input from any player.
        /// </summary>
        public bool IsPauseGame(PlayerIndex? controllingPlayer)
        {
            PlayerIndex playerIndex;

            return IsNewKeyPress(Keys.Escape, controllingPlayer, out playerIndex) ||
                   IsNewButtonPress(Buttons.Back, controllingPlayer, out playerIndex) ||
                   IsNewButtonPress(Buttons.Start, controllingPlayer, out playerIndex);
        }

        public bool IsMouseMoved(out int X, out int Y)
        {
            X = CurrentMouseState.X;
            Y = CurrentMouseState.Y;

            return !(CurrentMouseState.X == LastMouseState.X && CurrentMouseState.Y == LastMouseState.Y);

        }

        public enum MouseButton
        {
            None,
            Left,
            Middle,
            Right

        }

        //public bool IsMouseDown(out MouseButton mouseButton, out int X, out int Y)
        //{
        //    X = CurrentMouseState.X;
        //    Y = CurrentMouseState.Y;
        //    mouseButton = MouseButton.None;

        //    if (CurrentMouseState.LeftButton == ButtonState.Pressed)
        //    {
        //        mouseButton = MouseButton.Left;
        //        return true;
        //    }
        //    if (CurrentMouseState.RightButton == ButtonState.Pressed)
        //    {
        //        mouseButton = MouseButton.Right;
        //        return true;
        //    }
        //    if (CurrentMouseState.MiddleButton == ButtonState.Pressed)
        //    {
        //        mouseButton = MouseButton.Middle;
        //        return true;
        //    }
        //    return false;
        //}

        //public bool IsMouseUp(out MouseButton mouseButton, out int X, out int Y)
        //{
        //    X = CurrentMouseState.X;
        //    Y = CurrentMouseState.Y;
        //    mouseButton = MouseButton.None;

        //    if (CurrentMouseState.LeftButton == ButtonState.Released)
        //    {
        //        mouseButton = MouseButton.Left;
        //        return true;
        //    }
        //    if (CurrentMouseState.RightButton == ButtonState.Released)
        //    {
        //        mouseButton = MouseButton.Right;
        //        return true;
        //    }
        //    if (CurrentMouseState.MiddleButton == ButtonState.Released)
        //    {
        //        mouseButton = MouseButton.Middle;
        //        return true;
        //    }
        //    return false;
        //}

        //public bool IsMouseClick(out MouseButton mouseButton, out int X, out int Y)
        //{
        //    X = CurrentMouseState.X;
        //    Y = CurrentMouseState.Y;
        //    mouseButton = MouseButton.None;

        //    if (CurrentMouseState.LeftButton == ButtonState.Released
        //        && LastMouseState.LeftButton == ButtonState.Pressed)
        //    {
        //        mouseButton = MouseButton.Left;
        //        return true;
        //    }
        //    if (CurrentMouseState.RightButton == ButtonState.Released
        //        && LastMouseState.RightButton == ButtonState.Pressed)
        //    {
        //        mouseButton = MouseButton.Right;
        //        return true;
        //    }
        //    if (CurrentMouseState.MiddleButton == ButtonState.Released
        //        && LastMouseState.MiddleButton == ButtonState.Pressed)
        //    {
        //        mouseButton = MouseButton.Middle;
        //        return true;
        //    }
        //    return false;
        //}

        public bool IsMouseClicked(MouseButton mouseButton, out int X, out int Y)
        {
            X = CurrentMouseState.X;
            Y = CurrentMouseState.Y;

            switch (mouseButton)
	        {
                case MouseButton.Left:
                    if (CurrentMouseState.LeftButton == ButtonState.Released &&
                        LastMouseState.LeftButton == ButtonState.Pressed)
                    {
                        return true;
                    }
                    break;
                case MouseButton.Middle:
                    if (CurrentMouseState.MiddleButton == ButtonState.Released &&
                        LastMouseState.MiddleButton == ButtonState.Pressed)
                    {
                        return true;
                    }
                    break;
                case MouseButton.Right:
                    if (CurrentMouseState.RightButton == ButtonState.Released &&
                        LastMouseState.RightButton == ButtonState.Pressed)
                    {
                        return true;
                    }
                    break;
		        default:
                    break;
	        }
            return false;
        }

        public bool IsMousePressed(MouseButton mouseButton, out int X, out int Y)
        {
            X = CurrentMouseState.X;
            Y = CurrentMouseState.Y;

            switch (mouseButton)
            {
                case MouseButton.Left:
                    if (CurrentMouseState.LeftButton == ButtonState.Pressed)
                    {
                        return true;
                    }
                    break;
                case MouseButton.Middle:
                    if (CurrentMouseState.MiddleButton == ButtonState.Pressed)
                    {
                        return true;
                    }
                    break;
                case MouseButton.Right:
                    if (CurrentMouseState.RightButton == ButtonState.Pressed)
                    {
                        return true;
                    }
                    break;
                default:
                    break;
            }
            return false;
        }

        public bool IsMouseReleased(MouseButton mouseButton, out int X, out int Y)
        {
            X = CurrentMouseState.X;
            Y = CurrentMouseState.Y;

            switch (mouseButton)
            {
                case MouseButton.Left:
                    if (CurrentMouseState.LeftButton == ButtonState.Released)
                    {
                        return true;
                    }
                    break;
                case MouseButton.Middle:
                    if (CurrentMouseState.MiddleButton == ButtonState.Released)
                    {
                        return true;
                    }
                    break;
                case MouseButton.Right:
                    if (CurrentMouseState.RightButton == ButtonState.Released)
                    {
                        return true;
                    }
                    break;
                default:
                    break;
            }
            return false;
        }
        #endregion
    }
}
